﻿using System.Linq.Expressions;
using MySql.Simple.Mapper.Finders;

namespace MySql.Simple.Mapper.Linq.ExpressionVisitors
{
    /// <summary>
    /// Transforms <see cref="NewExpression"/> objects into MySQL syntax
    /// </summary>
    public class BinaryExpressionVisitor : IExpressionVisitor
    {
        /// <summary>
        /// Determines whether this instance can visit the specified expression.
        /// </summary>
        /// <param name="expression">The expression.</param>
        /// <returns>
        /// 	<c>true</c> if this instance can visit the specified expression; otherwise, <c>false</c>.
        /// </returns>
        public bool CanVisit(Expression expression)
        {
            return expression is BinaryExpression;
        }

        /// <summary>
        /// Visits the specified expression and returns a string representation of it's contents.
        /// </summary>
        /// <param name="expression">The expression.</param>
        /// <param name="query"></param>
        /// <returns></returns>
        public SqlQuery Visit(Expression expression, SqlQuery query)
        {
            var binaryExpression = (BinaryExpression) expression;

            if (binaryExpression.NodeType == ExpressionType.AndAlso)
            {
                if (binaryExpression.Left is BinaryExpression)
                {
                    var left = (BinaryExpression)binaryExpression.Left;

                    query.Where += Visit(left, query);
                }

                if (binaryExpression.Right is BinaryExpression)
                {
                    var right = (BinaryExpression)binaryExpression.Right;

                    if (!string.IsNullOrEmpty(query.Where)) query.Where += " AND ";

                    query.Where += Visit(right, query);
                }
            }
            else
            {
                query.Where += GetSqlCondition(binaryExpression);
            }

            return query;
        }

        private string GetSqlCondition(BinaryExpression binaryExpression)
        {
            var clause = string.Empty;

            var ma = (MemberExpression)binaryExpression.Left;

            var columns = ColumnFinder.Find(ma.Member.ReflectedType);

            clause += "`" + columns.Find(ma.Member.Name).Name + "`";

            clause += MethodToSql(binaryExpression);

            var right = new SqlGenerator().Generate(binaryExpression.Right, new SqlQuery());

            clause += right.Fields;

            return clause;
        }

        private static string MethodToSql(Expression expresssion)
        {
            string sql;

            switch (expresssion.NodeType)
            {
                case ExpressionType.Equal:
                    sql = " = ";
                    break;

                case ExpressionType.GreaterThan:
                    sql = " > ";
                    break;

                case ExpressionType.LessThan:
                    sql = " < ";
                    break;

                case ExpressionType.GreaterThanOrEqual:
                    sql = " >= ";
                    break;

                case ExpressionType.LessThanOrEqual:
                    sql = " <= ";
                    break;

                default:
                    sql = string.Empty;
                    break;
            }

            return sql;
        }
    }
}
